//
//  UIImage+SGExtension.swift
//  QingLiuSwift
//
//  Created by xyanl on 2024/11/11.
//  Copyright © 2024 考拉🐨. All rights reserved.
//

import Foundation
import SVGKit

extension UIImage: SpadgerCompatible {}

// MARK: - 保存图片
public extension Spadger where Base: UIImage {
    // MARK: 1.14、保存图片到相册
    /// 保存图片到相册
    func saveImageToPhotoAlbum(_ result: ((Bool) -> Void)?) {
        base.saveToPhotoAlbum(result)
    }
}

fileprivate extension UIImage {

    private struct SGRuntimeKey {
        static var saveBlockKey = UnsafeRawPointer("saveBlock".withCString { $0 })
    }
    private var saveBlock: ((Bool) -> Void)? {
        set {
            objc_setAssociatedObject(self, &SGRuntimeKey.saveBlockKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
        get {
            return objc_getAssociatedObject(self, &SGRuntimeKey.saveBlockKey) as? (Bool) -> Void
        }
    }

    /// 保存图片到相册
    func saveToPhotoAlbum(_ result: ((Bool) -> Void)?) {
        saveBlock = result
        UIImageWriteToSavedPhotosAlbum(self, self, #selector(saveImage(image:didFinishSavingWithError:contextInfo:)), nil)
    }

    @objc private func saveImage(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: AnyObject) {
        if error != nil {
            saveBlock?(false)
        } else {
            saveBlock?(true)
        }
    }
}

// MARK: - 五、二维码的处理
public extension Spadger where Base: UIImage {
    // MARK: 5.1、生成二维码图片
    /// 生成二维码图片
    /// - Parameters:
    ///   - content: 二维码里面的内容
    ///   - size: 二维码的大小
    ///   - logoSize: logo的大小
    ///   - logoImage: logo图片
    /// - Returns: 返回生成二维码图片
    static func qrImage(
        with content: String,
        size: CGSize,
        isLogo: Bool = true,
        logoSize: CGSize? = nil,
        logoImage: UIImage? = nil,
        logoRoundCorner: CGFloat? = nil
    ) -> UIImage? {
        // 1、创建名为"CIQRCodeGenerator"的CIFilter
        let filter = CIFilter(name: "CIQRCodeGenerator")
        // 2、将filter所有属性设置为默认值
        filter?.setDefaults()
        // 3、将所需尽心转为UTF8的数据，并设置给filter
        let data = content.data(using: String.Encoding.utf8)
        filter?.setValue(data, forKey: "inputMessage")
        // 4、设置二维码的纠错水平，越高纠错水平越高，可以污损的范围越大
        /*
         L: 7%
         M: 15%
         Q: 25%
         H: 30%
         */
        filter?.setValue("H", forKey: "inputCorrectionLevel")
        // 5、拿到二维码图片，此时的图片不是很清晰，需要二次加工
        guard let outPutImage = filter?.outputImage else { return nil }
        
        return getHDImgWithCIImage(with: outPutImage, size: size, isLogo: isLogo, logoSize: logoSize, logoImage: logoImage, logoRoundCorner: logoRoundCorner)
    }
    
    // MARK: 调整二维码清晰度，添加水印图片
    /// 调整二维码清晰度，添加水印图片
    /// - Parameters:
    ///   - image: 模糊的二维码图片
    ///   - size: 二维码的宽高
    ///   - logoSize: logo的大小
    ///   - logoImage: logo图片
    /// - Returns: 添加 logo 图片后，清晰的二维码图片
    private static func getHDImgWithCIImage(
        with image: CIImage,
        size: CGSize,
        isLogo: Bool = true,
        logoSize: CGSize?,
        logoImage: UIImage? = nil,
        logoRoundCorner: CGFloat? = nil
    ) -> UIImage? {
        let extent = image.extent.integral
        let scale = min(size.width / extent.width, size.height / extent.height)
        //1.创建bitmap
        let width = extent.width * scale
        let height = extent.height * scale
        
        // 创建基于GPU的CIContext对象,性能和效果更好
        let context = CIContext(options: nil)
        // 创建CoreGraphics image
        guard let bitmapImage = context.createCGImage(image, from: extent) else { return nil }
        
        // 创建一个DeviceGray颜色空间
        let cs = CGColorSpaceCreateDeviceGray()
        // CGBitmapContextCreate(void * _Nullable data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef  _Nullable space, uint32_t bitmapInfo)
        // width：图片宽度像素
        // height：图片高度像素
        // bitsPerComponent：每个颜色的比特值，例如在rgba-32模式下为8
        // bitmapInfo：指定的位图应该包含一个alpha通道
        let bitmapRef = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: cs, bitmapInfo: CGImageAlphaInfo.none.rawValue) //图形上下文，画布
        bitmapRef?.interpolationQuality = CGInterpolationQuality.none //写入质量
        bitmapRef?.scaleBy(x: scale, y: scale) //调整“画布”的缩放
        bitmapRef?.draw(bitmapImage, in: extent) //绘制图片
        
        //2.保存bitmap到图片
        guard let scaledImage = bitmapRef?.makeImage() else { return nil }
        
        // 清晰的二维码图片
        let outputImage = UIImage(cgImage: scaledImage)
        
        guard isLogo == true, let logoSize = logoSize, let logoImage = logoImage else {
            return outputImage
        }
        
        var newLogo: UIImage = logoImage
        if let newLogoRoundCorner = logoRoundCorner, let roundCornerLogo = logoImage.jk.isRoundCorner(radius: newLogoRoundCorner, byRoundingCorners: .allCorners, imageSize: logoSize) {
            newLogo = roundCornerLogo
        }
        // 防止size：(0, 0)崩溃
        var drawSize = outputImage.size
        if drawSize.width <= 0 || drawSize.height <= 0 {
            drawSize = CGSize(width: 1, height: 1)
        }
        // 给二维码加 logo 图
        UIGraphicsBeginImageContextWithOptions(drawSize, false, UIScreen.main.scale)
        outputImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        // 把水印图片画到生成的二维码图片上，注意尺寸不要太大（根据上面生成二维码设置的纠错程度设置），否则有可能造成扫不出来
        let waterImgW = logoSize.width
        let waterImgH = logoSize.height
        let waterImgX = (size.width - waterImgW) * 0.5
        let waterImgY = (size.height - waterImgH) * 0.5
        newLogo.draw(in: CGRect(x: waterImgX, y: waterImgY, width: waterImgW, height: waterImgH))
        let newPicture = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return newPicture
    }
    
    // MARK: 5.2、获取图片中二维码数组
    func getImageQRImage() -> [CIQRCodeFeature] {
        let qrcodeImg = self.base as UIImage
        let context = CIContext(options: nil)
        guard let ciImage: CIImage = CIImage(image:qrcodeImg),
              let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: context, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]),
              let features = detector.features(in: ciImage) as? [CIQRCodeFeature] else {
            return []
        }
        return features
    }
    
    /// 获取图片每个二维码里面的信息数组
    func getImageQRImageInfo() -> [String] {
        // 遍历所有的二维码，并拿出二维码里面的信息
        getImageQRImage().map { $0.messageString ?? ""}
    }
    
    /// 修正方向图片
    var fixOrientationImage: UIImage {
        if base.imageOrientation == .up {
            return base
        }
        UIGraphicsBeginImageContextWithOptions(base.size, false, base.scale)
        base.draw(in: CGRect(x: 0, y: 0, width: base.size.width, height: base.size.height))
        if let normalizedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() {
            UIGraphicsEndImageContext()
            return normalizedImage
        } else {
            return base
        }
    }
}


/// 未测试过
extension UIImage {
    // MARK: - 压缩图片大小
    func compressSize(with maxSize: Int) -> Data? {
        // 先判断当前质量是否满足要求，不满足再进行压缩
        guard var finallImageData = jpegData(compressionQuality: 1.0) else { return nil }
        if finallImageData.count / 1024 <= maxSize {
            return finallImageData
        }
        // 先调整分辨率
        var defaultSize = CGSize(width: 1024, height: 1024)
        guard let compressImage = scaleSize(defaultSize), let compressImageData = compressImage.jpegData(compressionQuality: 1.0) else {
            return nil
        }
        finallImageData = compressImageData
        
        // 保存压缩系数
        var compressionQualityArray = [CGFloat]()
        let avg: CGFloat = 1.0 / 250
        var value = avg
        var i: CGFloat = 250.0
        repeat {
            i -= 1
            value = i * avg
            compressionQualityArray.append(value)
        } while i >= 1
        
        // 调整大小，压缩系数数组compressionQualityArr是从大到小存储，思路：使用二分法搜索
        guard let halfData = halfFuntion(array: compressionQualityArray, image: compressImage, sourceData: finallImageData, maxSize: maxSize) else {
            return nil
        }
        finallImageData = halfData
        // 如果还是未能压缩到指定大小，则进行降分辨率
        while finallImageData.count == 0 {
            // 每次降100分辨率
            if defaultSize.width - 100 <= 0 || defaultSize.height - 100 <= 0 {
                break
            }
            defaultSize = CGSize(width: defaultSize.width - 100, height: defaultSize.height - 100)
            guard let lastValue = compressionQualityArray.last,
                  let newImageData = compressImage.jpegData(compressionQuality: lastValue),
                  let tempImage = UIImage(data: newImageData),
                  let tempCompressImage = tempImage.scaleSize(defaultSize),
                  let sourceData = tempCompressImage.jpegData(compressionQuality: 1.0),
                  let halfData = halfFuntion(array: compressionQualityArray, image: tempCompressImage, sourceData: sourceData, maxSize: maxSize)
            else {
                return nil
            }
            finallImageData = halfData
        }
        return finallImageData
    }
    
    /// 压缩图片
    /// - Parameters:
    ///   - width: 压缩后图片的宽度，默认：0，根据手机的scale等比缩小
    ///   - maxSize: 压缩后图片的最大存储
    /// - Returns: 压缩后的图片data，maxSize*0.9 < data.count < maxSize
    func compress(width: CGFloat = 0, maxSize: Int = 1024 * 1024 * 2) -> Data? {
        
        guard let data = self.jpegData(compressionQuality: 1) else { return nil }
        if data.count < maxSize {
            return data
        }
        
        let scale = UIScreen.main.scale
        var resize = CGSize(width: self.size.width / scale, height: self.size.height / scale)
        
        if width > 0 {
            let height = width * self.size.height / self.size.width
            resize = CGSize(width: width, height: height)
        }
        
        guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil) else { return nil }
        
        let maxPixelSize = max(resize.width, resize.height)
        let options = [
            kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
            kCGImageSourceCreateThumbnailFromImageAlways: true,
            kCGImageSourceCreateThumbnailWithTransform: true,
            kCGImageSourceShouldCacheImmediately: true,
        ] as [CFString : Any]
        
        // 改变image的size
        let resizedImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary).flatMap {
            UIImage(cgImage: $0)
        }
        
        var compression: CGFloat = 1
        var max: CGFloat = 1
        var min: CGFloat = 0
        
        var resultData: Data!
        for _ in 0..<6 {
            compression = (max + min) / 2
            resultData = resizedImage!.jpegData(compressionQuality: compression)!
            // print("data: \(resultData.count) b compression: \(compression)")
            if CGFloat(resultData.count) < CGFloat(maxSize) * 0.9 {
                min = compression
            } else if resultData.count > maxSize {
                max = compression
            } else {
                break
            }
        }
        
        // let size = CGFloat(resultData.count)/1024.0
        // print("result data: \(size) kb")
        return resultData
    }
    
    func resizeIO(resizeSize: CGSize) -> UIImage? {
        guard let imageData = self.pngData() else { return nil }
        guard let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil) else { return nil }
        
        let maxPixelSize = max(resizeSize.width, resizeSize.height)
        let options = [
            kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
            kCGImageSourceCreateThumbnailFromImageAlways: true,
            kCGImageSourceCreateThumbnailWithTransform: true,
            
        ] as [CFString : Any]
        
        let resizedImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary).flatMap {
            UIImage(cgImage: $0)
        }
        return resizedImage
    }
    
    
    // MARK: - 调整图片分辨率/尺寸（等比例缩放）
    
    func scaleSize(_ newSize: CGSize) -> UIImage? {
        let heightScale = size.height / newSize.height
        let widthScale = size.width / newSize.width
        
        var finallSize = CGSize(width: size.width, height: size.height)
        if widthScale > 1.0, widthScale > heightScale {
            finallSize = CGSize(width: size.width / widthScale, height: size.height / widthScale)
        } else if heightScale > 1.0, widthScale < heightScale {
            finallSize = CGSize(width: size.width / heightScale, height: size.height / heightScale)
        }
        UIGraphicsBeginImageContext(CGSize(width: Int(finallSize.width), height: Int(finallSize.height)))
        draw(in: CGRect(x: 0, y: 0, width: finallSize.width, height: finallSize.height))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }
    
    // MARK: - 二分法
    private func halfFuntion(array: [CGFloat], image: UIImage, sourceData: Data, maxSize: Int) -> Data? {
        var tempFinallImageData = sourceData
        var finallImageData = Data()
        var start = 0
        var end = array.count - 1
        var index = 0
        
        var difference = Int.max
        while start <= end {
            index = start + (end - start) / 2
            guard let data = image.jpegData(compressionQuality: array[index]) else {
                return nil
            }
            tempFinallImageData = data
            let sizeOrigin = tempFinallImageData.count
            let sizeOriginKB = sizeOrigin / 1024
            if sizeOriginKB > maxSize {
                start = index + 1
            } else if sizeOriginKB < maxSize {
                if maxSize - sizeOriginKB < difference {
                    difference = maxSize - sizeOriginKB
                    finallImageData = tempFinallImageData
                }
                if index <= 0 {
                    break
                }
                end = index - 1
            } else {
                break
            }
        }
        return finallImageData
    }
}

/// 未测试过
extension UIImage {
    /// 智能压缩图片大小
    func smartCompressImage() -> Data? {
        guard let finallImageData = jpegData(compressionQuality: 1.0) else { return nil }
        if finallImageData.count / 1024 <= 300 {
            return finallImageData
        }
        var width = size.width
        var height = size.height
        let longSide = max(width, height)
        let shortSide = min(width, height)
        let scale = shortSide / longSide
        if shortSide < 1080 || longSide < 1080 {
            return jpegData(compressionQuality: 0.5)
        } else {
            if width < height {
                width = 1080
                height = 1080 / scale
            } else {
                width = 1080 / scale
                height = 1080
            }
            UIGraphicsBeginImageContext(CGSize(width: width, height: height))
            draw(in: CGRect(x: 0, y: 0, width: width, height: height))
            let compressImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return compressImage?.jpegData(compressionQuality: 0.5)
        }
    }
}

/// 未测试过
extension UIImage {
    /// 灰度算法
    enum GrayscaleAlgorithm {
        /// 平均值
        case average
        /// rec601标准
        case rec601
        /// rec709标准
        case rec709
        /// rec2100标准
        case rec2100
        /// 最大值
        case max
        /// 最小值
        case min
        /// 分量法—红
        case red
        /// 分量法—绿
        case green
        /// 分量法—蓝
        case blue
    }
    
    /// 灰度图
    func grayscale(_ type: GrayscaleAlgorithm) -> UIImage? {
        guard let cgImage else {
            return nil
        }
        
        let imageWidth = Int(size.width * scale)
        let imageHeight = Int(size.height * scale)
        
        guard let pixelBuffer: CVPixelBuffer = {
            let pixelBufferAttributes = [
                kCVPixelFormatCGImageCompatibility: true,
                kCVPixelBufferCGBitmapContextCompatibilityKey: true,
                kCVPixelBufferIOSurfacePropertiesKey: [String: Any](),
            ] as CFDictionary
            
            var pixelBuffer: CVPixelBuffer?
            CVPixelBufferCreate(
                kCFAllocatorDefault,
                imageWidth,
                imageHeight,
                kCVPixelFormatType_32ARGB,
                pixelBufferAttributes,
                &pixelBuffer
            )
            return pixelBuffer
        }() else {
            return nil
        }
        
        CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
        
        let pixelBufferData = CVPixelBufferGetBaseAddress(pixelBuffer)
        
        guard let context = CGContext(
            data: pixelBufferData,
            width: imageWidth,
            height: imageHeight,
            bitsPerComponent: 8,
            bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer),
            space: CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue
        ) else { return nil }
        
        context.draw(cgImage, in: CGRect(origin: .zero, size: size.applying(CGAffineTransform(scaleX: scale, y: scale))))
        
        let data = unsafeBitCast(pixelBufferData, to: UnsafeMutablePointer<UInt8>.self)
        
        let row = CVPixelBufferGetBytesPerRow(pixelBuffer) / 4
        let height = CVPixelBufferGetHeight(pixelBuffer)
        
        for y in 0 ..< height {
            for x in 0 ..< row {
                let offset = 4 * (y * row + x)
                
                let gray: UInt8 = {
                    let red = CGFloat(data[offset + 1])
                    let green = CGFloat(data[offset + 2])
                    let blue = CGFloat(data[offset + 3])
                    
                    switch type {
                    case .average:
                        let value = 1.0 / 3.0
                        return UInt8(red * value + green * value + blue * value)
                    case .rec601:
                        return UInt8(red * 0.299 + green * 0.587 + blue * 0.114)
                    case .rec709:
                        return UInt8(red * 0.2126 + green * 0.7152 + blue * 0.0722)
                    case .rec2100:
                        return UInt8(red * 0.2627 + green * 0.6780 + blue * 0.0593)
                    case .max:
                        return UInt8(max(max(red, green), blue))
                    case .min:
                        return UInt8(min(min(red, green), blue))
                    case .red:
                        return UInt8(red)
                    case .green:
                        return UInt8(green)
                    case .blue:
                        return UInt8(blue)
                    }
                }()
                data[offset + 1] = gray
                data[offset + 2] = gray
                data[offset + 3] = gray
            }
        }
        CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
        guard let cgImage = context.makeImage() else {
            return nil
        }
        return UIImage(cgImage: cgImage)
    }
    
    
    static func gradualShadowImage(_ havingSize: CGSize) -> UIImage? {
        let layer = CAGradientLayer.init()
        layer.contentsScale = UIScreen.main.scale
        let blackColor = UIColor.black
        layer.colors = [
            blackColor.withAlphaComponent(0).cgColor,
            blackColor.withAlphaComponent(0.3).cgColor,
            blackColor.withAlphaComponent(0.4).cgColor,
            blackColor.withAlphaComponent(0.5).cgColor,
            blackColor.withAlphaComponent(0.6).cgColor
        ]
        layer.startPoint = CGPoint(x: 0, y: 1)
        layer.endPoint = CGPoint(x: 0, y: 0)
        layer.locations = [0.1, 0.3, 0.5, 0.7, 0.9]
        layer.borderWidth = 0.0
        layer.frame = CGRect(origin: .zero, size: havingSize)
        let format = UIGraphicsImageRendererFormat()
        format.opaque = false
        format.scale = UIScreen.main.scale
        let renderer = UIGraphicsImageRenderer(size: havingSize, format: format)
        let image = renderer.image { context in
            layer.render(in: context.cgContext)
        }
        return image
    }
}


extension UIImage {
  
    /// svg 转 image
    /// - Parameters:
    ///   - name: 图片名
    ///   - size: 图片大小
    ///   - tintColor: 颜色
    /// - Returns: UIImage
    static func svgImage(name: String, size: CGSize, tintColor: UIColor) -> UIImage? {
        
        guard let svgImage = SVGKImage(named: name) else {
            return nil
        }
        
        svgImage.size = size
        
        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        
        let alphaInfo: CGImageAlphaInfo = svgImage.uiImage.cgImage!.alphaInfo
        
        let opaque = alphaInfo == .noneSkipLast || alphaInfo == .noneSkipFirst || alphaInfo == .none
        
        UIGraphicsBeginImageContextWithOptions(svgImage.size, opaque, svgImage.scale)
        
        let context: CGContext = UIGraphicsGetCurrentContext()!
        
        context.translateBy(x: 0, y: svgImage.size.height)
        
        context.scaleBy(x: 1.0, y: -1.0)
        
        context.setBlendMode(.normal)
        
        context.clip(to: rect, mask: svgImage.uiImage.cgImage!)
        
        context.setFillColor(tintColor.cgColor)
        
        context.fill(rect)
        
        let imageOut = UIGraphicsGetImageFromCurrentImageContext()
        
        UIGraphicsEndImageContext()
        
        return imageOut!
    }

}

extension UIImage {
    
    /// 获取视频的第一帧
    /// - Parameter url: 视频本地地址
    /// - Returns: 视频的封面
    static func imageFromVideo(_ url: String) -> UIImage? {
        
        let Url = URL(string: url)
        // 获取本地视频
        let asset = AVURLAsset(url: Url!)
        
        // 生成视频截图
        let generator = AVAssetImageGenerator(asset: asset)
        generator.appliesPreferredTrackTransform = true
        
        let time = CMTimeMake(value: 0, timescale: 600)
        var actualTime: CMTime = .zero
        if let cgImage = try? generator.copyCGImage(at: time, actualTime: &actualTime) {
            let image = UIImage(cgImage: cgImage)
            return image
        }
        return nil
    }
    
    //MARK: 获取第一帧当封面
    static func getFirstGen(asset:AVURLAsset) -> UIImage? {
        let assetGen = AVAssetImageGenerator(asset: asset)
        assetGen.appliesPreferredTrackTransform = true
        let time = CMTimeMake(value: 0, timescale: 600)
        var actualTime:CMTime = .zero
        if let cgImage = try? assetGen.copyCGImage(at: time, actualTime: &actualTime) {
            let image = UIImage(cgImage: cgImage)
            return image
        }
        return nil
    }
}
